package com.sikulifang.frequency.service.impl;

import com.sikulifang.frequency.core.InterfaceTypeEnum;
import com.sikulifang.frequency.service.IFrequency;
import com.sikulifang.frequency.utils.FrequencyUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import static com.sikulifang.frequency.utils.FrequencyUtil.rest;

/**
 * 多类型线程控制
 *
 * @author zhoul 2020-11-20
 * @since 3.0.0
 */
@Slf4j
@Data
public class MultiTypeFrequency implements IFrequency {

    /**
     * 查询
     * true - 固定延迟时长
     * false - 动态延迟时长
     */
    private boolean queryInterval;
    /**
     * 查询：接口延迟时间
     */
    private Long queryDelayTime;
    /**
     * 查询等待的最大时间(ms)
     */
    private Long queryWaitingTime;

    /**
     * 交易
     * true - 固定延迟时长
     * false - 动态延迟时长
     */
    private boolean tradeInterval;
    /**
     * 交易：接口延迟时间
     */
    private Long tradeDelayTime;
    /**
     * 交易等待的最大时间(ms)
     */
    private Long tradeWaitingTime;

    /**
     * 是否开启查询和交易的间隔
     * true - 开启
     * false - 关闭
     */
    private boolean interval;
    /**
     * 查询和交易的间隔：间隔时长(ms)
     */
    private Long intervalTime;

    /**
     * 查询信号量
     */
    private final Semaphore querySemaphore = new Semaphore(1, true);

    /**
     * 交易信号量
     */
    private final Semaphore tradeSemaphore = new Semaphore(1, true);

    private final Lock lock = new ReentrantLock();

    /**
     * 统计当前的请求类型
     */
    private final AtomicReference<InterfaceTypeEnum> reference = new AtomicReference<>(
            InterfaceTypeEnum.query
    );

    @Override
    public void acquire(String type) throws InterruptedException {

        switch (type) {
            // 查询类型
            case TYPE_QUERY -> {
                FrequencyUtil.acquire(querySemaphore, queryWaitingTime);

                // 如果有交易请求时 暂停查询
                FrequencyUtil.suspend(tradeSemaphore::hasQueuedThreads, intervalTime);

                if (tradeSemaphore.hasQueuedThreads()) {
                    Thread.yield();
                }
                // 加锁 保证 交易和查询时间间隔准确
                lock(queryWaitingTime);
                swapSleep(InterfaceTypeEnum.query);
            }

            // 交易类型
            case TYPE_TRADE -> {
                FrequencyUtil.acquire(tradeSemaphore, tradeWaitingTime);
                // 加锁 保证 交易和查询时间间隔准确
                lock(tradeWaitingTime);
                swapSleep(InterfaceTypeEnum.trade);
            }
        }
    }

    /**
     * Description: 交易和查询间隔休眠
     * <p>
     * Date: 2023年7月27日, 0027 16:08
     *
     * @param typeEnum 当前请交易的类型
     */
    private void swapSleep(InterfaceTypeEnum typeEnum) {
        // 获取上一次的 请求数据
        InterfaceTypeEnum oldGap = reference.get();

        if (interval && oldGap != typeEnum) {
            reference.compareAndSet(oldGap, typeEnum);
            rest(intervalTime);
        }
    }

    @Override
    public void delay(String type, long time) {
        switch (type) {
            case TYPE_QUERY -> {
                if (queryInterval) {
                    // 固定休眠
                    rest(queryDelayTime);
                } else {
                    if (time >= 0 && time < queryDelayTime) {
                        rest(queryDelayTime - time);
                    }
                }
            }
            case TYPE_TRADE -> {
                if (tradeInterval) {
                    rest(tradeDelayTime);
                } else {
                    if (time >= 0 && time < tradeDelayTime) {
                        rest(tradeDelayTime - time);
                    }
                }
            }
        }
    }

    @Override
    public void release(String type) {
        lock.unlock();
        if (TYPE_QUERY.equals(type)) {
            if (querySemaphore.availablePermits() == 0) {
                querySemaphore.release();
            }
        }
        if (TYPE_TRADE.equals(type)) {
            if (tradeSemaphore.availablePermits() == 0) {
                tradeSemaphore.release();
            }
        }
    }

    private void lock(Long waitingTime) throws InterruptedException {
        // 判断是否需要进行超时等待
        if (waitingTime != null && waitingTime > 0) {
            boolean tryLock = lock.tryLock(waitingTime, TimeUnit.MILLISECONDS);
            if (!tryLock) {
                // 获取锁失败
                throw new InterruptedException();
            }
        } else {
            lock.lock();
        }
    }
}
